1   /*
2    * Copyright (C) 2008 The Guava Authors
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    * http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package com.google.common.collect.testing.testers;
18  
19  import static com.google.common.collect.testing.features.CollectionFeature.ALLOWS_NULL_VALUES;
20  import static com.google.common.collect.testing.features.CollectionFeature.SUPPORTS_REMOVE;
21  import static com.google.common.collect.testing.features.CollectionSize.ONE;
22  import static com.google.common.collect.testing.features.CollectionSize.ZERO;
23  
24  import com.google.common.annotations.GwtCompatible;
25  import com.google.common.collect.testing.AbstractCollectionTester;
26  import com.google.common.collect.testing.MinimalCollection;
27  import com.google.common.collect.testing.features.CollectionFeature;
28  import com.google.common.collect.testing.features.CollectionSize;
29  
30  import java.util.Arrays;
31  import java.util.Collection;
32  import java.util.Collections;
33  import java.util.List;
34  
35  /**
36   * A generic JUnit test which tests {@code retainAll} operations on a
37   * collection. Can't be invoked directly; please see
38   * {@link com.google.common.collect.testing.CollectionTestSuiteBuilder}.
39   *
40   * @author Chris Povirk
41   */
42  @SuppressWarnings("unchecked") // too many "unchecked generic array creations"
43  @GwtCompatible
44  public class CollectionRetainAllTester<E> extends AbstractCollectionTester<E> {
45  
46    /**
47     * A collection of elements to retain, along with a description for use in
48     * failure messages.
49     */
50    private class Target {
51      private final Collection<E> toRetain;
52      private final String description;
53  
54      private Target(Collection<E> toRetain, String description) {
55        this.toRetain = toRetain;
56        this.description = description;
57      }
58  
59      @Override public String toString() {
60        return description;
61      }
62    }
63  
64    private Target empty;
65    private Target disjoint;
66    private Target superset;
67    private Target nonEmptyProperSubset;
68    private Target sameElements;
69    private Target partialOverlap;
70    private Target containsDuplicates;
71    private Target nullSingleton;
72  
73    @Override public void setUp() throws Exception {
74      super.setUp();
75  
76      empty = new Target(emptyCollection(), "empty");
77      /*
78       * We test that nullSingleton.retainAll(disjointList) does NOT throw a
79       * NullPointerException when disjointList does not, so we can't use
80       * MinimalCollection, which throws NullPointerException on calls to
81       * contains(null).
82       */
83      List<E> disjointList = Arrays.asList(samples.e3, samples.e4);
84      disjoint
85          = new Target(disjointList, "disjoint");
86      superset
87          = new Target(MinimalCollection.of(
88              samples.e0, samples.e1, samples.e2, samples.e3, samples.e4),
89              "superset");
90      nonEmptyProperSubset
91          = new Target(MinimalCollection.of(samples.e1), "subset");
92      sameElements
93          = new Target(Arrays.asList(createSamplesArray()), "sameElements");
94      containsDuplicates = new Target(
95          MinimalCollection.of(samples.e0, samples.e0, samples.e3, samples.e3),
96          "containsDuplicates");
97      partialOverlap
98          = new Target(MinimalCollection.of(samples.e2, samples.e3),
99              "partialOverlap");
100     nullSingleton
101         = new Target(Collections.<E>singleton(null), "nullSingleton");
102   }
103 
104   // retainAll(empty)
105 
106   @CollectionFeature.Require(SUPPORTS_REMOVE)
107   @CollectionSize.Require(ZERO)
108   public void testRetainAll_emptyPreviouslyEmpty() {
109     expectReturnsFalse(empty);
110     expectUnchanged();
111   }
112 
113   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
114   @CollectionSize.Require(ZERO)
115   public void testRetainAll_emptyPreviouslyEmptyUnsupported() {
116     expectReturnsFalseOrThrows(empty);
117     expectUnchanged();
118   }
119 
120   @CollectionFeature.Require(SUPPORTS_REMOVE)
121   @CollectionSize.Require(absent = ZERO)
122   public void testRetainAll_emptyPreviouslyNonEmpty() {
123     expectReturnsTrue(empty);
124     expectContents();
125     expectMissing(samples.e0, samples.e1, samples.e2);
126   }
127 
128   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
129   @CollectionSize.Require(absent = ZERO)
130   public void testRetainAll_emptyPreviouslyNonEmptyUnsupported() {
131     expectThrows(empty);
132     expectUnchanged();
133   }
134 
135   // retainAll(disjoint)
136 
137   @CollectionFeature.Require(SUPPORTS_REMOVE)
138   @CollectionSize.Require(ZERO)
139   public void testRetainAll_disjointPreviouslyEmpty() {
140     expectReturnsFalse(disjoint);
141     expectUnchanged();
142   }
143 
144   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
145   @CollectionSize.Require(ZERO)
146   public void testRetainAll_disjointPreviouslyEmptyUnsupported() {
147     expectReturnsFalseOrThrows(disjoint);
148     expectUnchanged();
149   }
150 
151   @CollectionFeature.Require(SUPPORTS_REMOVE)
152   @CollectionSize.Require(absent = ZERO)
153   public void testRetainAll_disjointPreviouslyNonEmpty() {
154     expectReturnsTrue(disjoint);
155     expectContents();
156     expectMissing(samples.e0, samples.e1, samples.e2);
157   }
158 
159   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
160   @CollectionSize.Require(absent = ZERO)
161   public void testRetainAll_disjointPreviouslyNonEmptyUnsupported() {
162     expectThrows(disjoint);
163     expectUnchanged();
164   }
165 
166   // retainAll(superset)
167 
168   @CollectionFeature.Require(SUPPORTS_REMOVE)
169   public void testRetainAll_superset() {
170     expectReturnsFalse(superset);
171     expectUnchanged();
172   }
173 
174   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
175   public void testRetainAll_supersetUnsupported() {
176     expectReturnsFalseOrThrows(superset);
177     expectUnchanged();
178   }
179 
180   // retainAll(subset)
181 
182   @CollectionFeature.Require(SUPPORTS_REMOVE)
183   @CollectionSize.Require(absent = {ZERO, ONE})
184   public void testRetainAll_subset() {
185     expectReturnsTrue(nonEmptyProperSubset);
186     expectContents(nonEmptyProperSubset.toRetain);
187   }
188 
189   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
190   @CollectionSize.Require(absent = {ZERO, ONE})
191   public void testRetainAll_subsetUnsupported() {
192     expectThrows(nonEmptyProperSubset);
193     expectUnchanged();
194   }
195 
196   // retainAll(sameElements)
197 
198   @CollectionFeature.Require(SUPPORTS_REMOVE)
199   public void testRetainAll_sameElements() {
200     expectReturnsFalse(sameElements);
201     expectUnchanged();
202   }
203 
204   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
205   public void testRetainAll_sameElementsUnsupported() {
206     expectReturnsFalseOrThrows(sameElements);
207     expectUnchanged();
208   }
209 
210   // retainAll(partialOverlap)
211 
212   @CollectionFeature.Require(SUPPORTS_REMOVE)
213   @CollectionSize.Require(absent = {ZERO, ONE})
214   public void testRetainAll_partialOverlap() {
215     expectReturnsTrue(partialOverlap);
216     expectContents(samples.e2);
217   }
218 
219   @CollectionFeature.Require(absent = SUPPORTS_REMOVE)
220   @CollectionSize.Require(absent = {ZERO, ONE})
221   public void testRetainAll_partialOverlapUnsupported() {
222     expectThrows(partialOverlap);
223     expectUnchanged();
224   }
225 
226   // retainAll(containsDuplicates)
227 
228   @CollectionFeature.Require(SUPPORTS_REMOVE)
229   @CollectionSize.Require(ONE)
230   public void testRetainAll_containsDuplicatesSizeOne() {
231     expectReturnsFalse(containsDuplicates);
232     expectContents(samples.e0);
233   }
234 
235   @CollectionFeature.Require(SUPPORTS_REMOVE)
236   @CollectionSize.Require(absent = {ZERO, ONE})
237   public void testRetainAll_containsDuplicatesSizeSeveral() {
238     expectReturnsTrue(containsDuplicates);
239     expectContents(samples.e0);
240   }
241 
242   // retainAll(nullSingleton)
243 
244   @CollectionFeature.Require(SUPPORTS_REMOVE)
245   @CollectionSize.Require(ZERO)
246   public void testRetainAll_nullSingletonPreviouslyEmpty() {
247     expectReturnsFalse(nullSingleton);
248     expectUnchanged();
249   }
250 
251   @CollectionFeature.Require(SUPPORTS_REMOVE)
252   @CollectionSize.Require(absent = ZERO)
253   public void testRetainAll_nullSingletonPreviouslyNonEmpty() {
254     expectReturnsTrue(nullSingleton);
255     expectContents();
256   }
257 
258   @CollectionFeature.Require({SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
259   @CollectionSize.Require(ONE)
260   public void testRetainAll_nullSingletonPreviouslySingletonWithNull() {
261     initCollectionWithNullElement();
262     expectReturnsFalse(nullSingleton);
263     expectContents(createArrayWithNullElement());
264   }
265 
266   @CollectionFeature.Require({SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
267   @CollectionSize.Require(absent = {ZERO, ONE})
268   public void testRetainAll_nullSingletonPreviouslySeveralWithNull() {
269     initCollectionWithNullElement();
270     expectReturnsTrue(nullSingleton);
271     expectContents(nullSingleton.toRetain);
272   }
273 
274   // nullSingleton.retainAll()
275 
276   @CollectionFeature.Require({SUPPORTS_REMOVE, ALLOWS_NULL_VALUES})
277   @CollectionSize.Require(absent = ZERO)
278   public void testRetainAll_containsNonNullWithNull() {
279     initCollectionWithNullElement();
280     expectReturnsTrue(disjoint);
281     expectContents();
282   }
283 
284   // retainAll(null)
285 
286   /*
287    * AbstractCollection fails the retainAll(null) test when the subject
288    * collection is empty, but we'd still like to test retainAll(null) when we
289    * can. We split the test into empty and non-empty cases. This allows us to
290    * suppress only the former.
291    */
292 
293   @CollectionFeature.Require(SUPPORTS_REMOVE)
294   @CollectionSize.Require(ZERO)
295   public void testRetainAll_nullCollectionReferenceEmptySubject() {
296     try {
297       collection.retainAll(null);
298       // Returning successfully is not ideal, but tolerated.
299     } catch (NullPointerException expected) {
300     }
301   }
302 
303   @CollectionFeature.Require(SUPPORTS_REMOVE)
304   @CollectionSize.Require(absent = ZERO)
305   public void testRetainAll_nullCollectionReferenceNonEmptySubject() {
306     try {
307       collection.retainAll(null);
308       fail("retainAll(null) should throw NullPointerException");
309     } catch (NullPointerException expected) {
310     }
311   }
312 
313   private void expectReturnsTrue(Target target) {
314     String message
315         = Platform.format("retainAll(%s) should return true", target);
316     assertTrue(message, collection.retainAll(target.toRetain));
317   }
318 
319   private void expectReturnsFalse(Target target) {
320     String message
321         = Platform.format("retainAll(%s) should return false", target);
322     assertFalse(message, collection.retainAll(target.toRetain));
323   }
324 
325   private void expectThrows(Target target) {
326     try {
327       collection.retainAll(target.toRetain);
328       String message = Platform.format("retainAll(%s) should throw", target);
329       fail(message);
330     } catch (UnsupportedOperationException expected) {
331     }
332   }
333 
334   private void expectReturnsFalseOrThrows(Target target) {
335     String message
336         = Platform.format("retainAll(%s) should return false or throw", target);
337     try {
338       assertFalse(message, collection.retainAll(target.toRetain));
339     } catch (UnsupportedOperationException tolerated) {
340     }
341   }
342 }